import 'dart:async';
import 'package:flutter/material.dart';

void main() {
  runApp(const PerfApp());
}

class PerfApp extends StatelessWidget {
  const PerfApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Text Write Perf Test',
      theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.blue),
      home: const PerfHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class PerfHomePage extends StatefulWidget {
  const PerfHomePage({super.key});

  @override
  State<PerfHomePage> createState() => _PerfHomePageState();
}

class _PerfHomePageState extends State<PerfHomePage> {
  final TextEditingController _viewer = TextEditingController();
  final ScrollController _scroll = ScrollController();
  final StringBuffer _buffer = StringBuffer();

  // 可调参数
  int _batchLines = 200;     // 每批次写入行数
  int _intervalMs = 16;      // 周期写入的间隔（毫秒），16ms≈60fps

  // 统计指标
  int _totalLines = 0;
  final Stopwatch _stopwatch = Stopwatch();
  Timer? _timer;

  // —— 写入核心：将一批行追加到 StringBuffer，然后整体赋给 TextEditingController ——
  void _appendBatch(int lines) {
    final startIndex = _totalLines + 1;
    final endIndex = _totalLines + lines;
    for (int i = startIndex; i <= endIndex; i++) {
      // 每行格式：时间戳 + 递增序号，便于肉眼确认顺序与丢行情况
      _buffer.writeln('${DateTime.now().toIso8601String()}  #$i  sample payload');
    }
    _totalLines += lines;

    // 一次性更新文本，并把光标放到末尾（自动滚动到底）
    final text = _buffer.toString();
    _viewer.value = TextEditingValue(
      text: text,
      selection: TextSelection.collapsed(offset: text.length),
    );

    // 主动滚到底部（双保险）
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (_scroll.hasClients) {
        _scroll.jumpTo(_scroll.position.maxScrollExtent);
      }
    });

    setState(() {}); // 刷新指标显示
  }

  // 突发写入：尽可能快地写很多次，直至达到目标或手动停止
  Future<void> _burstWrite(int totalTargetLines) async {
    _stop();
    _clearMetrics(keepText: true);
    _stopwatch.start();

    int remaining = totalTargetLines;
    // 这里用微任务节流，避免长帧卡住 UI（介于“尽可能快”和“还能响应”之间）
    while (remaining > 0) {
      if (!mounted) break;
      final n = remaining >= _batchLines ? _batchLines : remaining;
      _appendBatch(n);
      remaining -= n;
      // 把控制权还给事件循环，让 UI 有机会渲染
      await Future<void>.delayed(Duration.zero);
    }

    _stopwatch.stop();
    setState(() {});
  }

  // 持续写入：按固定间隔定时写入
  void _startStreaming() {
    if (_timer != null) return;
    _clearMetrics(keepText: true);
    _stopwatch.start();
    _timer = Timer.periodic(Duration(milliseconds: _intervalMs), (_) {
      _appendBatch(_batchLines);
    });
    setState(() {});
  }

  void _stop() {
    _timer?.cancel();
    _timer = null;
    if (_stopwatch.isRunning) {
      _stopwatch.stop();
    }
    setState(() {});
  }

  void _clearAll() {
    _stop();
    _buffer.clear();
    _viewer.clear();
    _clearMetrics(keepText: false);
  }

  void _clearMetrics({required bool keepText}) {
    if (!keepText) {
      _totalLines = 0;
    }
    _stopwatch
      ..reset()
      ..stop();
    setState(() {});
  }

  double get _elapsedSeconds =>
      _stopwatch.elapsedMilliseconds == 0 ? 0.0 : _stopwatch.elapsedMilliseconds / 1000.0;

  String get _rateStr {
    if (_elapsedSeconds == 0) return '-';
    final r = _totalLines / _elapsedSeconds;
    return r.toStringAsFixed(0);
  }

  @override
  void dispose() {
    _stop();
    _viewer.dispose();
    _scroll.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final elapsed = _stopwatch.elapsedMilliseconds;

    return Scaffold(
      appBar: AppBar(
        title: const Text('多行文本写入性能测试（Flutter）'),
        actions: [
          IconButton(
            tooltip: '清空全部',
            onPressed: _clearAll,
            icon: const Icon(Icons.delete_sweep),
          ),
        ],
      ),
      body: Column(
        children: [
          // 控制面板
          Padding(
            padding: const EdgeInsets.fromLTRB(12, 8, 12, 6),
            child: Column(
              children: [
                Wrap(
                  crossAxisAlignment: WrapCrossAlignment.center,
                  spacing: 12,
                  runSpacing: 8,
                  children: [
                    // 每批次行数
                    _NumberField(
                      label: '每批次行数',
                      initial: _batchLines,
                      min: 1,
                      max: 100000,
                      onChanged: (v) => setState(() => _batchLines = v),
                    ),
                    // 间隔毫秒
                    _NumberField(
                      label: '间隔(ms, 持续写入)',
                      initial: _intervalMs,
                      min: 1,
                      max: 2000,
                      onChanged: (v) => setState(() => _intervalMs = v),
                    ),
                    // 突发目标行数
                    _BurstStarter(
                      onStart: (target) => _burstWrite(target),
                    ),
                    FilledButton.icon(
                      onPressed: _startStreaming,
                      icon: const Icon(Icons.play_circle),
                      label: const Text('开始持续写入'),
                    ),
                    OutlinedButton.icon(
                      onPressed: _stop,
                      icon: const Icon(Icons.stop_circle_outlined),
                      label: const Text('停止'),
                    ),
                    OutlinedButton.icon(
                      onPressed: () => _clearMetrics(keepText: true),
                      icon: const Icon(Icons.refresh),
                      label: const Text('清指标(保留文本)'),
                    ),
                  ],
                ),
                const SizedBox(height: 8),
                // 指标
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text('累计行数：$_totalLines'),
                    Text('耗时：${elapsed}ms'),
                    Text('速度：$_rateStr 行/秒'),
                    if (_timer != null) const Chip(label: Text('持续写入中')),
                  ],
                ),
              ],
            ),
          ),
          const Divider(height: 1),
          // 多行文本框（展示区）
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Scrollbar(
                controller: _scroll,
                child: TextField(
                  controller: _viewer,
                  scrollController: _scroll,
                  readOnly: true,
                  expands: true,
                  maxLines: null,
                  minLines: null,
                  decoration: const InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: '写入展示区（只读）',
                    alignLabelWithHint: true,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _NumberField extends StatefulWidget {
  final String label;
  final int initial;
  final int min;
  final int max;
  final ValueChanged<int> onChanged;

  const _NumberField({
    required this.label,
    required this.initial,
    required this.min,
    required this.max,
    required this.onChanged,
  });

  @override
  State<_NumberField> createState() => _NumberFieldState();
}

class _NumberFieldState extends State<_NumberField> {
  late final TextEditingController _c;
  String? _error;

  @override
  void initState() {
    super.initState();
    _c = TextEditingController(text: widget.initial.toString());
  }

  @override
  void dispose() {
    _c.dispose();
    super.dispose();
  }

  void _onSubmitted(String s) {
    final v = int.tryParse(s);
    if (v == null || v < widget.min || v > widget.max) {
      setState(() => _error = '范围 ${widget.min}-${widget.max}');
    } else {
      setState(() => _error = null);
      widget.onChanged(v);
    }
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 180,
      child: TextField(
        controller: _c,
        onSubmitted: _onSubmitted,
        keyboardType: TextInputType.number,
        decoration: InputDecoration(
          labelText: widget.label,
          errorText: _error,
          border: const OutlineInputBorder(),
          isDense: true,
        ),
      ),
    );
    }
}

class _BurstStarter extends StatefulWidget {
  final ValueChanged<int> onStart;
  const _BurstStarter({required this.onStart});

  @override
  State<_BurstStarter> createState() => _BurstStarterState();
}

class _BurstStarterState extends State<_BurstStarter> {
  final TextEditingController _c = TextEditingController(text: '10000');

  @override
  void dispose() {
    _c.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Row(mainAxisSize: MainAxisSize.min, children: [
      SizedBox(
        width: 160,
        child: TextField(
          controller: _c,
          keyboardType: TextInputType.number,
          decoration: const InputDecoration(
            labelText: '突发目标行数',
            border: OutlineInputBorder(),
            isDense: true,
          ),
        ),
      ),
      const SizedBox(width: 8),
      FilledButton(
        onPressed: () {
          final n = int.tryParse(_c.text.trim());
          if (n != null && n > 0) {
            widget.onStart(n);
          }
        },
        child: const Text('开始突发写入'),
      )
    ]);
  }
}